package org.example.chat.aop;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.example.chat.aop.annotation.AccessLimiter;
import org.example.chat.exception.RequestLimitedException;
import org.example.chat.security.entity.LoginUser;
import org.example.chat.utils.BaseUtils;
import org.example.chat.utils.CommonUtils;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
@Aspect
public class AccessLimiterAspect {

    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private BaseUtils baseUtils;

    @Pointcut("@annotation(org.example.chat.aop.annotation.AccessLimiter)")
    public void limiterPointCut() {

    }

    @Before("limiterPointCut()")
    public void doLimit(JoinPoint joinPoint) {
        // 1. 获取限流器名称
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String simpleMethodName = methodSignature.getName();
        String className = methodSignature.getDeclaringTypeName();
        String limiterName = className + "." + simpleMethodName;
        // 2. 获取注解参数
        Method method = methodSignature.getMethod();
        AccessLimiter annotation = method.getAnnotation(AccessLimiter.class);
        int rate = annotation.rate();
        int interval = annotation.rateInterval();
        boolean limitByClientIp = annotation.isLimitByClientIp();
        if (limitByClientIp) {
            // 获取客户端ip
            String clientIp = baseUtils.getClientIp();
            clientIp = clientIp.replaceAll(":","-");
            if (StringUtils.isNotEmpty(clientIp)) {
                limiterName = limiterName + "(" + clientIp + ")";
            }
        }
        // 3. 获取限流器
        RRateLimiter limiter = redissonClient.getRateLimiter(limiterName);
        /*
         * RateType.OVERALL‌：表示全局限流，即对整个系统或集群进行限流。
         * RateType.PER_CLIENT‌：表示单Client限流，即对单个客户端进行限流，可以认为是单机限流。
         */
        limiter.trySetRate(RateType.OVERALL, rate, interval, RateIntervalUnit.SECONDS);
        boolean flag = limiter.tryAcquire();
        if (!flag) {
            throw new RequestLimitedException();
        }
    }
}
